package com.zhendong.gulimall.seckill.scheduled;

import com.zhendong.gulimall.seckill.Service.SeckillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author 孟享广
 * @date 2021-02-20 10:55 上午
 * @description 秒杀商品到定时上架
 *  每天晚上3:00 ：上架最近三天需要秒杀的商品
 *  当天0点 - 23：59
 *  明天0点 - 23：59
 *  后天0点 - 23：59
 */
@Slf4j
@Service
public class SecKillSkuScheduled {

    private final String UPLOAD_LOCK = "seckill:upload:lock";

    @Autowired
    SeckillService seckillService;

    @Autowired
    RedissonClient redissonClient;

    /**
     * TODO zhen重点：幂等性处理
     * 1、这里加了一把分布式锁，保证不同的机器执行定时任务时都依次执行
     * 2、在上架方法seckillService.uploadSeckillSkuLatest3Days()中，分别有缓存活动信息saveSessionInfos和缓存商品信息saveSessionSkuInfos，这连个方法也要做幂等性处理，这两方法处理的方式就是：
     *    先判断redis中有没有对应的key，没有的话才缓存，有就不往redis里放了，这就是幂等处理
     * 3、老师总结：有人说两个方法已经有幂等处理，能不能不要这把分布式锁：不行的。因为如果没有锁，两个机器同时执行到reids去getKey那一行并且没有key就会继续上架，都往Semaphore中放值就出错了
     */


//    //异步任务 + 下
//    @Async
    //定时任务
    @Scheduled(cron = "0/10 * * * * ?")
    public void uploadSeckillSkuLatest3Days() {

        log.info("上架秒杀商品信息....");
        //重复上架无需处理
        //加分布式锁 所有的业务执行完成，状态已经更新完成。释放，所以后期他人获取到就会拿到最新的状态(原子性)
        //TODO zhen这里加分布式锁用redisson，而上架接口的幂等性是是判断有没有那个key是用redisTemplate
        //TODO zhen这里锁的粒度比较大，不是基于某个skuID，而是基于所有的上架功能
        RLock lock = redissonClient.getLock(UPLOAD_LOCK);
        lock.lock(10, TimeUnit.MINUTES);
        try {
            seckillService.uploadSeckillSkuLatest3Days();
        } finally {
            lock.unlock();
        }
    }

}
